/**
 *  Copyright (C) 2013  Piotr Szczepa�ski
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package edu.pw.elka.gtsna.centrality.degree;

import edu.pw.elka.gtsna.centrality.NodeCentrality;
import edu.pw.elka.gtsna.community_detection.CentralityForCommunityStructure;
import edu.pw.elka.gtsna.community_detection.CommunityStructure;
import edu.pw.elka.gtsna.graph.Graph;
import static edu.pw.elka.gtsna.utilities.MathBinomialCoefficient.binomial;;
/**
 * @author Piotr Lech Szczepa�ski
 * @author P.Szczepanski@ii.pw.edu.pl 
 *
 *
 *
 *	This class implements the coalitional semivalue degree centrality 
 *	which was introduced in paper:
 */
 public abstract class CSemivalueNodeCentrality extends NodeCentrality implements CentralityForCommunityStructure {

	 protected CommunityStructure cs;

	protected abstract double f(int node);
	
	protected abstract double beta(int k);
	
	protected abstract double alpha(int l, int coalitionSize);
	
	public CSemivalueNodeCentrality(Graph graph, CommunityStructure cs) {
		super(graph);
		this.cs = cs;
	}

	@Override
	public void computeCentrality() {
		for(int node: graph.getAllNodes()){		
			centralities.put(node, 0.0);
			
			int Cj = cs.getNodeCommunityNumber(node);
			for(int k=0; k <= cs.size()-1;k++){
				for(int l=0; l <= cs.size(node)-1;l++){
					double MCkl = 0.0;
					double spaceSize = binomial(cs.size()-1,k)*binomial(cs.size(node)-1,l);

					for(int neighbour: graph.getNeigbours(node)){
						if (cs.getNodeCommunityNumber(neighbour) == Cj) {
							MCkl+= (f(neighbour)*binomial(cs.size()-1-graph.getInterCommunityDegree(neighbour,cs),k)*
							binomial(cs.size(node)-1-graph.getIntraCommunityDegree(neighbour, cs, Cj),l))/spaceSize;
						} else {
							MCkl+= (f(neighbour)*binomial(cs.size()-1-graph.getInterCommunityDegree(neighbour,cs),k)*
									binomial(cs.size(node)-graph.getIntraCommunityDegree(neighbour, cs, Cj),l))/spaceSize;
						}						
					}
					MCkl-= f(node);
					MCkl+= (f(node)*binomial(cs.size()-1-graph.getInterCommunityDegree(node,cs),k)*
							binomial(cs.size(node)-1-graph.getIntraCommunityDegree(node, cs),l))/spaceSize;
					centralities.put(node, centralities.get(node) + beta(k)*alpha(l,cs.size(node))*MCkl);
				}	
			}
    	}
	}


	@Override
	public CommunityStructure getCommunityStructure() {
		return cs;
	}


	@Override
	public void setCommunityStructure(CommunityStructure cs) {
		this.cs = cs;
	}

}
